package pl.edu.mimuw.irs.rcp.util;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.ResourceBundle;
import java.util.Set;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;

public class Logger {

	// *** Static attributes

	public static final String MSGBOX_EXCEPTIONS = "msgbox.exceptions";
	
	public static final String LOGGER_EXCEPTION_MESSAGE = "Failed to generate message. This problem is caused by the following objects:";
	
	private static boolean msgBoxExceptions;
	private static ResourceBundle bundle;
	
	// *** Static initialization
	
	static {
		
		bundle = ResourceBundle.getBundle(MessageCode.BUNDLE);
		
		if (bundle.containsKey(MSGBOX_EXCEPTIONS)) {
			msgBoxExceptions = Boolean.valueOf(bundle.getString(MSGBOX_EXCEPTIONS));
		}
	}
	
	// *** Methods
	public static void handle(MessageCode messageCode, Exception ex, Object... opts) {
		handle(messageCode.getCode(), ex, opts);
	}

	public static void handle(MessageCode messageCode, Object... opts) {
		handle(messageCode.getCode(), (Exception)null, opts);
	}
	
	public static void handle(String messageCode, Object... opts) {
		handle(messageCode, (Exception)null, opts);
	}
	
	public static void handle(String messageCode, Exception exception, Object... opts) {

		if (bundle == null) return;
			
		String fullMessageCode = null;
		
		for (String key : Collections.list(bundle.getKeys())) {
			if (key.startsWith(messageCode + ".")) {
				fullMessageCode = key;
				break;
			}
		}
		
		if (fullMessageCode == null) {
			handleUnknown(messageCode, opts);
			return;
		}
		
		String modifiersString = fullMessageCode.substring(messageCode.length() + 1);
		String[] modifiers = modifiersString.split("\\.");
		
		Set<Flag> flags = new LinkedHashSet<Flag>();
		for (String modifier : modifiers) {
			try {
				flags.add(Flag.valueOf(modifier.toUpperCase()));
			} catch (IllegalArgumentException iae) {
				handleUnknown(messageCode, modifier);
				return;
			}
		}
		
		Priority priority = Priority.INFO;
		
		if (flags.contains(Flag.INFO)) {
			priority = Priority.INFO;
		} else if (flags.contains(Flag.WARN)) {
			priority = Priority.WARN;
		} else if (flags.contains(Flag.ERROR)) {
			priority = Priority.ERROR;
		}
		
		if (flags.contains(Flag.MSGBOX)) {
			Shell shell = getShell();
			MessageBox msgBox = null;
			StringBuffer message = new StringBuffer(bundle.getString(fullMessageCode));
			if (msgBoxExceptions && exception != null) {
				message.append("\n" + exception + "(Cause: " + exception.getCause() + "). ");
			}
			for (Object opt : opts) {
				message.append("\n" + opt.toString());
			}
			switch (priority) {
			case INFO:
				msgBox = new MessageBox(shell, SWT.OK | SWT.ICON_INFORMATION);
				msgBox.setText(UITexts.get(MessageCode.INFO_TITLE));
				break;
			case WARN:
				msgBox = new MessageBox(shell, SWT.OK | SWT.ICON_WARNING);
				msgBox.setText(UITexts.get(MessageCode.WARNING_TITLE));
				break;
			case ERROR:
				msgBox = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
				msgBox.setText(UITexts.get(MessageCode.ERROR_TITLE));
				break;
			}
			msgBox.setMessage(message.toString());
			msgBox.open();
		}
		
		// XXX : logowanie błędów na konsolę/do pliku
	}
	
	private static void handleUnknown(Object... opts) {
		Shell shell = getShell();
		MessageBox msgBox = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
		StringBuffer message = new StringBuffer(LOGGER_EXCEPTION_MESSAGE);
		for (Object opt : opts) {
			message.append("\n" + opt.toString());
		}
		msgBox.setMessage(message.toString());
		msgBox.setText("Error");
		
		msgBox.open();
	}
	
	private static Shell getShell() {
		Display display = Display.getCurrent();
		if (display == null) return new Shell();
		Shell shell = display.getActiveShell();
		if (shell == null) shell = new Shell();
		//Shell[] shells = display.getShells();
		//if (shells.length > 0) shell = shells[shells.length - 1];
		return shell;
	}
	
	public enum Priority {
		INFO,
		WARN,
		ERROR,
	}
	
	public enum Flag {
		INFO,
		WARN,
		ERROR,
		MSGBOX
	}
}
